/*++

Copyright (c) 1994-2003  Microsoft Corporation

Module Name:

    PARSEPJL.C

Abstract:

    Handles parsing of PJL printer response streams into token\value pairs.

--*/


/*
Currently returns tokens for (see enum in parsepjl.h for token values):
@PJL ECHO MSSYNC # ->#

@PJL INFO MEMORY
TOTAL=#   ->#
LARGEST=# ->#

@PJL INFO STATUS
CODE=#  ->#
DISPLAY=# (not returned)
ONLINE=TRUE (or FALSE) -> 1 or 0 returned

@PJL INQUIRE INTRAY?SIZE   (? is 1,2,3 or 4)
LEGAL(or other PJL paper size) ->constant from DM... list in PRINT.H

@PJL INFO CONFIG
MEMORY=# ->#

@PJL USTATUS JOB
END -> returns token with zero for value

@PJL USTATUS JOB
NAME="MSJOB #" ->#

added

@PJL USTATUS DEVICE
CODE=#  ->#
DISPLAY=# (not returned)
ONLINE=TRUE (or FALSE) -> 1 or 0 returned

*/

#include "precomp.h"

#define FF 12
#define CR 13
#define LF 10
#define TAB 9
#define SPACE 32

#define OK_IF_FF_FOUND    TRUE
#define ERROR_IF_FF_FOUND FALSE
#define TOKEN_BASE_NOT_USED 0
#define ACTION_NOT_USED 0
#define PARAM_NOT_USED 0
/* returned as value for TOKEN_USTATUS_JOB_END */
#define VALUE_RETURED_FOR_VALUELESS_TOKENS  0

extern KeywordType readBackCommandKeywords[];
extern KeywordType infoCatagoryKeywords[];
extern KeywordType inquireVariableKeywords[];
extern KeywordType traySizeKeywords[];
extern KeywordType echoKeywords[];
extern KeywordType infoConfigKeywords[];
extern KeywordType ustatusKeywords[];
extern KeywordType ustatusJobKeywords[];
extern KeywordType ustatusDeviceKeywords[];

/* Fuctions called when a string in keyword is found */
void TokenFromParamValueFromNumberFF
   (__in ParseVarsType *pParseVars, ParamType);
void SetNewList(__in ParseVarsType *pParseVars,
   ParamType);
void GetTotalAndLargestFF(__in ParseVarsType *pParseVars,ParamType param);
void GetCodeAndOnlineFF(__in ParseVarsType *pParseVars,ParamType param);
void GetTokenFromIndexSetNewList(__in ParseVarsType *pParseVars,ParamType param);
void SetValueFromParamFF(__in ParseVarsType *pParseVars,ParamType param);
void SetValueFromParam(__in ParseVarsType *pParseVars,ParamType param);
void GetTokenFromIndexValueFromNumberEOLFromParam(__in ParseVarsType *pParseVars,ParamType param);
void GetTokenFromIndexValueFromBooleanEOL(__in ParseVarsType *pParseVars,ParamType param);
void GetTokenFromIndexValueFromStringEOL(__in ParseVarsType *pParseVars,ParamType param);


/* Fuctions called when no string in a keywords list is found */
void ActionNotFoundSkipPastFF(__in ParseVarsType *pParseVars);
void ActionNotFoundSkipCFLFandIndentedLines(__in ParseVarsType *pParseVars);


/* Helper Functions */
void StoreToken(__in ParseVarsType *pParseVars, DWORD dwToken);
BOOL StoreTokenValueAndAdvancePointer
   (__in ParseVarsType *pParseVars, UINT_PTR dwValue);
void  ExpectFinalCRLFFF(__in ParseVarsType *pParseVars);
BOOL  SkipPastNextCRLF(__in ParseVarsType *pParseVars);
int GetPositiveInteger(__in ParseVarsType *pParseVars);
BOOL AdvancePointerPastString
   (__in ParseVarsType *pParseVars, __in LPSTR pString);
BOOL SkipOverSpaces(__in ParseVarsType *pParseVars);
int LookForKeyword(__in ParseVarsType *pParseVars);
BOOL ExpectString(__in ParseVarsType *pParseVars, __in LPSTR pString);
BOOL SkipPastFF(__in ParseVarsType *pParseVars);
void ExpectFinalFF(__in ParseVarsType *pParseVars);

/* Helper Strings */
char lpCRLF[] = "\r\n";
char lpQuoteCRLF[] = "\"\r\n";

/*
Below are the Lists that drive the parsing.  The main loop of this
parser looks through the keywords in the current list and tries to
match the keyword string to the current input stream.

If a keyword is found then the function corresponding to the Action in
the keyword is called.

If a FF is found in the input stream rather than a keyword, then the
parser returns.  The return value is determined using the bFormFeedOk
element of the ListType structure.

If no keyword from the list is found then the function corresponding
to the notFoundAction is called.

The tokenBaseValue element is a number to which the index in the
keyword's list of strings will added to calculate the token number
corresponding to the indexed string.
*/

ListType readBackCommandList =
   {
   ERROR_IF_FF_FOUND,
   ACTION_IF_NOT_FOUND_SKIP_PAST_FF,
   TOKEN_BASE_NOT_USED,
   readBackCommandKeywords /* INFO, ECHO, INQUIRE ... */
   };

ListType infoCatagoryList =
   {
   ERROR_IF_FF_FOUND,
   ACTION_IF_NOT_FOUND_SKIP_PAST_FF,
   TOKEN_BASE_NOT_USED,
   infoCatagoryKeywords  /* MEMORY STATUS CONFIG ... */
   };


ListType infoConfigList =
   {
   OK_IF_FF_FOUND,
   ACTION_IF_NOT_FOUND_SKIP_CFLF_AND_INDENTED_LINES,
   PJL_TOKEN_INFO_CONFIG_BASE,
   infoConfigKeywords  /* MEMORY= ... */
   };

ListType inquireVariableList =
   {
   ERROR_IF_FF_FOUND,
   ACTION_IF_NOT_FOUND_SKIP_PAST_FF,
   PJL_TOKEN_INQUIRE_BASE,
   inquireVariableKeywords /* INTRAY1SIZE ...*/
   };


ListType echoList =
   {
   OK_IF_FF_FOUND,
   ACTION_IF_NOT_FOUND_SKIP_PAST_FF,
   TOKEN_BASE_NOT_USED,
   echoKeywords /* MSSYNC ...*/
   };


ListType traySizeList =
   {
   ERROR_IF_FF_FOUND,
   ACTION_IF_NOT_FOUND_SKIP_PAST_FF,
   TOKEN_BASE_NOT_USED,
   traySizeKeywords /* LEGAL, C5 ...*/
   };

ListType ustatusList =
   {
   OK_IF_FF_FOUND,
   ACTION_IF_NOT_FOUND_SKIP_PAST_FF,
   PJL_TOKEN_USTATUS_JOB_BASE,
   ustatusKeywords  /* JOB ... */
   };


ListType ustatusJobList =
   {
   OK_IF_FF_FOUND,
   ACTION_IF_NOT_FOUND_SKIP_PAST_FF,
   PJL_TOKEN_USTATUS_JOB_BASE,
   ustatusJobKeywords  /* END ... */
   };

ListType ustatusDeviceList =
   {
   OK_IF_FF_FOUND,
   ACTION_IF_NOT_FOUND_SKIP_PAST_FF,
   PJL_TOKEN_USTATUS_DEVICE_BASE,
   ustatusDeviceKeywords  /* END ... */
   };


/* Command strings that can follow @PJL USTATUS */
KeywordType ustatusKeywords[] =
   {
      {"JOB\r\n", ACTION_TOKEN_FROM_INDEX_SET_NEW_LIST, &ustatusJobList},
      {"DEVICE\r\n", ACTION_TOKEN_FROM_INDEX_SET_NEW_LIST, &ustatusDeviceList},
//    {"DEVICE\r\n", ACTION_GET_CODE_AND_ONLINE_FF, PARAM_NOT_USED},
      {"TIMED\r\n", ACTION_TOKEN_FROM_INDEX_SET_NEW_LIST, &ustatusDeviceList},
      NULL
   };


/* Command strings that can follow @PJL USTATUS JOB */
KeywordType ustatusJobKeywords[] =
   {
      {"END\r\n", ACTION_SET_VALUE_FROM_PARAM, VALUE_RETURED_FOR_VALUELESS_TOKENS},
      {"NAME=\"MSJOB ", ACTION_GET_TOKEN_FROM_INDEX_VALUE_FROM_NUMBER_EOL_FROM_PARAM, (struct ListTypeTag *)lpQuoteCRLF},
      NULL
   };


/* command strings that can follow @PJL USTATUS DEVICE */
KeywordType ustatusDeviceKeywords[] =
   {
      {"CODE=", ACTION_GET_TOKEN_FROM_INDEX_VALUE_FROM_NUMBER_EOL_FROM_PARAM, (struct ListTypeTag *)lpCRLF},
      {"DISPLAY=", ACTION_GET_TOKEN_FROM_INDEX_VALUE_FROM_STRING_EOL, (struct ListTypeTag *)lpCRLF},
      {"ONLINE=", ACTION_GET_TOKEN_FROM_INDEX_VALUE_FROM_BOOLEAN_EOL, (struct ListTypeTag *)lpCRLF},
      NULL
   };


/* Command strings that can follow @PJL */
KeywordType readBackCommandKeywords[] =
   {
      {"INFO", ACTION_SET_NEW_LIST, &infoCatagoryList},
      {"ECHO", ACTION_SET_NEW_LIST, &echoList},
      {"INQUIRE", ACTION_SET_NEW_LIST, &inquireVariableList},
      {"USTATUS", ACTION_SET_NEW_LIST, &ustatusList},
      NULL
   };


/* Command strings that can follow @PJL ECHO (Microsoft specific-NOT PJL!) */
KeywordType echoKeywords[] =
   {
      {"MSSYNC", ACTION_TOKEN_FROM_PARAM_VALUE_FROM_NUMBER_FF,
         (struct ListTypeTag *)(INT_PTR)TOKEN_ECHO_MSSYNC_NUMBER},
      NULL
   };

/* Catagory strings that can follow @PJL INFO */
KeywordType infoCatagoryKeywords[] =
   {
      {"MEMORY\r\n", ACTION_GET_TOTAL_AND_LARGEST_FF, PARAM_NOT_USED},
      {"STATUS\r\n", ACTION_GET_CODE_AND_ONLINE_FF, PARAM_NOT_USED},
      {"CONFIG\r\n", ACTION_SET_NEW_LIST, &infoConfigList},
      NULL
   };

/* Catagory strings that can follow @PJL INFO */
KeywordType infoConfigKeywords[] =
   {
      {"MEMORY=", ACTION_GET_TOKEN_FROM_INDEX_VALUE_FROM_NUMBER_EOL_FROM_PARAM, (struct ListTypeTag *)lpCRLF},
      {"MEMORY = ", ACTION_GET_TOKEN_FROM_INDEX_VALUE_FROM_NUMBER_EOL_FROM_PARAM, (struct ListTypeTag *)lpCRLF},
      NULL
   };

/* TRUE or FALSE strings */
KeywordType FALSEandTRUEKeywords[] =
   {
      {"FALSE", ACTION_NOT_USED, PARAM_NOT_USED},
      {"TRUE",  ACTION_NOT_USED, PARAM_NOT_USED},
      NULL
   };

/* strings that can follow @PJL INQUIRE */
KeywordType inquireVariableKeywords[] =
   {
      {"INTRAY1SIZE\r\n", ACTION_TOKEN_FROM_INDEX_SET_NEW_LIST, &traySizeList},
      {"INTRAY2SIZE\r\n", ACTION_TOKEN_FROM_INDEX_SET_NEW_LIST, &traySizeList},
      {"INTRAY3SIZE\r\n", ACTION_TOKEN_FROM_INDEX_SET_NEW_LIST, &traySizeList},
      {"INTRAY4SIZE\r\n", ACTION_TOKEN_FROM_INDEX_SET_NEW_LIST, &traySizeList},
      NULL
   };

/* strings that can follow @PJL INQUIRE INTRAY?SIZE */
/* the parameters are the Microsoft defined token values for paper size */
KeywordType traySizeKeywords[] =
   {
      {"LETTER",    ACTION_SET_VALUE_FROM_PARAM_FF, (struct ListTypeTag *)DMPAPER_LETTER},
      {"LEGAL",     ACTION_SET_VALUE_FROM_PARAM_FF, (struct ListTypeTag *)DMPAPER_LEGAL},
      {"A4",        ACTION_SET_VALUE_FROM_PARAM_FF, (struct ListTypeTag *)DMPAPER_A4},
      {"EXECUTIVE", ACTION_SET_VALUE_FROM_PARAM_FF, (struct ListTypeTag *)DMPAPER_EXECUTIVE},
      {"COM10",     ACTION_SET_VALUE_FROM_PARAM_FF, (struct ListTypeTag *)DMPAPER_ENV_10},
      {"MONARCH",   ACTION_SET_VALUE_FROM_PARAM_FF, (struct ListTypeTag *)DMPAPER_ENV_MONARCH},
      {"C5",        ACTION_SET_VALUE_FROM_PARAM_FF, (struct ListTypeTag *)DMPAPER_ENV_C5},
      {"DL",        ACTION_SET_VALUE_FROM_PARAM_FF, (struct ListTypeTag *)DMPAPER_ENV_DL},
      {"B5",        ACTION_SET_VALUE_FROM_PARAM_FF, (struct ListTypeTag *)DMPAPER_ENV_B5},
      NULL
   };

void (*pfnNotFoundActions[])(ParseVarsType *pParseVars) =
   {
   ActionNotFoundSkipPastFF,
   ActionNotFoundSkipCFLFandIndentedLines
   };


void (*pfnFoundActions[])(ParseVarsType *pParseVars, ParamType param) =
   {
   TokenFromParamValueFromNumberFF,
   SetNewList,
   GetTotalAndLargestFF,
   GetCodeAndOnlineFF,
   GetTokenFromIndexSetNewList,
   SetValueFromParamFF,
   GetTokenFromIndexValueFromNumberEOLFromParam,
   SetValueFromParam,
   GetTokenFromIndexValueFromBooleanEOL,
   GetTokenFromIndexValueFromStringEOL
   };

PJLTOPRINTERSTATUS PJLToStatus[] =
{
    { 10001,0x0 },  // clear status - printer is ready
    { 10002,0x0 },  // clear status - check ONLINE=TRUE or FALSE
    { 11002,0x0 },  // LJ4 sends this code for 00 READY
    { 40022,PORT_STATUS_PAPER_JAM    },
    { 40034,PORT_STATUS_PAPER_PROBLEM},
    { 40079,PORT_STATUS_OFFLINE      },
    { 40019,PORT_STATUS_OUTPUT_BIN_FULL},

    { 10003,PORT_STATUS_WARMING_UP   },
    { 10006,PORT_STATUS_TONER_LOW    },
    { 40038,PORT_STATUS_TONER_LOW    },

    { 30016,PORT_STATUS_OUT_OF_MEMORY},
    { 40021,PORT_STATUS_DOOR_OPEN    },
    { 30078,PORT_STATUS_POWER_SAVE   },

    //
    // Entries added by MuhuntS
    //
    { 41002, PORT_STATUS_PAPER_PROBLEM}, // Load plain
    { 35078, PORT_STATUS_POWER_SAVE},
    {0, 0}

};



/* GetPJLTokens
This function parses a single ASCII PJL command and returns token/value pairs.
Complete PJL commands must begin with '@PJL' and end with a <FF>.

The function result returns one of the following values:
   0 = STATUS_REACHED_END_OF_COMMAND_OK
   1 = STATUS_END_OF_STRING
   2 = STATUS_SYNTAX_ERROR
   3 = STATUS_ATPJL_NOT_FOUND,
   4 = STATUS_NOT_ENOUGH_ROOM_FOR_TOKENS

Also returned through the parameters are:
 1] *plpInPJL:
    If STATUS_REACHED_END_OF_COMMAND_OK
      will point to the character past the first <FF> (FF = form feed).
    If STATUS_END_OF_STRING
      will point to the terminator that was found before any <FF>.
    Else
      undefined

 2] *pnTokenParsed will contain the number of pairs returned in *pToken.

 3] pToken will contain *pnTokenParsed  token pairs

If there are characters belonging to another command trailing the first
then the caller should call again for the new command.  If only part of
the new command may be present, then the caller may want to copy the
characters of the new command to the beginning of the buffer, and then read
the necessary additional characters onto the end before resubmitting the
complete command to this function for parsing.  Note that the *plpInPJL
tells the caller where the next command would begin.


If the end of the string is encountered before the trailing <FF> is found then
the function returns with *plpInPJL pointing to the terminator.
If the caller wants the command parsed into
token\value pairs it should resubmit the string once the characters
which complete the command have been appended.


Operation:
----------
Lists drive the parsing.  The main loop of this
parser looks through the keywords of the current list and tries to
match the keyword string to the current input stream.

If a keyword is found then the function corresponding to the Action in
the keyword is called.

If no keyword from the list is found then the function corresponding
to the notFoundAction is called.

*/

DWORD GetPJLTokens(
    __in    LPSTR lpInPJL,
            DWORD nTokenInBuffer,
    __out_ecount(nTokenInBuffer) TokenPairType *pToken,
    __out   DWORD *pnTokenParsed,
    __out   LPSTR *plpInPJL
)
{
   /* The parseVars variables are put into a structure so that they can be
      passed efficiently to all the helper functions.
    */
   ParseVarsType parseVars;
   BOOL bFoundKeyword;
   DWORD i, keywordIndex = 0;
   KeywordType *pKeyword;
   DWORD dwNotFoundAction = 0;
   BOOL bNotFoundAction = FALSE;

   /* The first list to look for is the commands that can follow
      @PJL
    */
   parseVars.arrayOfLists[0] = &readBackCommandList;
   parseVars.arrayOfLists[1] = NULL;

   parseVars.pInPJL_Local = lpInPJL;
   parseVars.nTokenInBuffer_Local = 0;
   parseVars.nTokenLeft = nTokenInBuffer;
   parseVars.pToken_Local = pToken;
   parseVars.status = STATUS_CONTINUE;

   if (!AdvancePointerPastString(&parseVars, "@PJL"))
      {
      parseVars.status = STATUS_ATPJL_NOT_FOUND;
      }

   while (parseVars.status == STATUS_CONTINUE)
      {
      /* Look for next input keyword in currently valid lists.
         Sometimes may need to look for the next input keyword in more
         then one list.
       */
      bFoundKeyword = FALSE;
      bNotFoundAction = FALSE;
      for (i=0; (parseVars.pCurrentList = parseVars.arrayOfLists[i])!=NULL; i++)
         {
         dwNotFoundAction = parseVars.pCurrentList->dwNotFoundAction;
         bNotFoundAction = TRUE;
         /* Skip over spaces to start of next keyword string */
         if ( !SkipOverSpaces(&parseVars) )
            {
            /* Either the input stream has ended or FF was found */
            if (parseVars.status == STATUS_REACHED_FF)
               {
               /* Finding a FF here may or may not be an error,
                  the field in the current list tells us which
                */

               if ( parseVars.pCurrentList->bFormFeedOK )
                  {
                  parseVars.status = STATUS_REACHED_END_OF_COMMAND_OK;
                  }
               else
                  {
                  parseVars.status = STATUS_SYNTAX_ERROR;
                  }
               }
            break;
            }
         /* Look for keyword in current keywords */
         parseVars.pCurrentKeywords = parseVars.pCurrentList->pListOfKeywords;
         keywordIndex = LookForKeyword(&parseVars);
         if ( keywordIndex!=-1 )
            {
            bFoundKeyword = TRUE;
            break;
            }
         }

      if ( parseVars.status!=STATUS_CONTINUE )
         {
         /* We are finished processing commands */
         break;
         }

      if ( bFoundKeyword )
         /* do action from keyword */
         {
         pKeyword = &parseVars.pCurrentKeywords[keywordIndex];
         (*pfnFoundActions[pKeyword->dwAction])(&parseVars, pKeyword->param);
         }
      else if (bNotFoundAction)
         /* An action was not found, call the not found routine */
         {
         (*pfnNotFoundActions[dwNotFoundAction])(&parseVars);
         }
      }

   /* We are done parsing the input command, now we return the information */

   DBG_MSG(DBG_TRACE, ("ParseVars.status = %d\n", parseVars.status));

   /* Fill in returned values and return with success */
   *pnTokenParsed = parseVars.nTokenInBuffer_Local;
   *plpInPJL = parseVars.pInPJL_Local;

   return(parseVars.status);
}


/*
int LookForKeyword(ParseVarsType *pParseVars)

This function looks through the current keyword list in search of a
keyword that matches the characters in the input stream pointed to
by pParseVars->pInPJL_Local.

If a match is found:
        The index of the match in the pKeyword is returned.
        pParseVars->pInPJL_Local is advanced past the last matching character.
        pParseVars->dwKeywordIndex is set to item number in list

If no match is found:
        The return value is -1.
        pParseVars->pInPJL_Local is unchanged.
*/
int LookForKeyword(__in ParseVarsType *pParseVars)
{
LPSTR   pInStart = pParseVars->pInPJL_Local;
LPSTR   pIn;
DWORD   dwKeywordIndex = 0;
BOOL    bFoundMatch = FALSE;
BYTE    c;
KeywordType *pKeywords = pParseVars->pCurrentKeywords;
LPSTR   pKeywordString;

while ( (pKeywordString=pKeywords[dwKeywordIndex++].lpsz)!=NULL )
{
   DBG_MSG(DBG_TRACE, ("LookForIn=%hs\n", pInStart));
   DBG_MSG(DBG_TRACE, ("Keyword=%hs\n", pKeywordString));

   pIn = pInStart;
   while ( (c=*pKeywordString++)!=0 )
      {
      if ( c!=*pIn++ )
         {
         break;
         }
      }

   if ( c==0 )
      {
      bFoundMatch = TRUE;
      pParseVars->pInPJL_Local = pIn;
      pParseVars->dwFoundIndex = dwKeywordIndex-1;
      break;
      }
   }

   DBG_MSG(DBG_TRACE, ("LookForOut=%hs\n", pParseVars->pInPJL_Local));

   return( (bFoundMatch)?dwKeywordIndex-1:-1 );
}


/*
BOOL AdvancePointerPastString(ParseVarsType *pParseVars, LPSTR pString)

This function looks through the input stream for a match with pString.

If a match is found:
   pParseVars->pInPJL_Local is set to point just past the string.
   the return value is TRUE
   (pParseVars->status is unchanged)

If the end of input is encountered before the string is found then
   pParseVars->pInPJL_Local is set to point to the terminating 0.
   the return value is FALSE
   pParseVars->status is set to STATUS_END_OF_STRING

If an FF is encountered before the string is found then
   pParseVars->pInPJL_Local is set to point just past the FF.
   the return value is FALSE
   pParseVars->status is set to STATUS_REACHED_FF
*/
BOOL AdvancePointerPastString(__in  ParseVarsType *pParseVars, __in LPSTR pString)
{
LPSTR pIn = pParseVars->pInPJL_Local;
LPSTR pS = pString;
BYTE  s, in = 0;

   while ( ((s=*pS) != 0) && ((in=*pIn)!=0) && (in!=FF) )
      {
      if ( s==in )
         {
         pS++; /* point to next char in string to look for match */
         }
      else
         {
         pS = pString; /* start over looking for start of string */
         }
      pIn++;
      }

   if ( s==0 )
      {
      /* The whole string matched  */
      /* point to character after string in input */
      pParseVars->pInPJL_Local = pIn;
      return(TRUE);
      }

   if ( in==FF )
      {
      pParseVars->status = STATUS_REACHED_FF;
      pParseVars->pInPJL_Local = pIn+1;
      }
   else
      {
      pParseVars->status = STATUS_END_OF_STRING;
      pParseVars->pInPJL_Local = pIn;
      }

   return(FALSE);
}



/*
BOOL SkipOverSpaces(ParseVarsType &parseVars)
This function skips over spaces in the input stream until a non-space
character (FF and NULL are special cases) is found.

If a non-space character is found then
   pParseVars->pInPJL_Local is set to point to the first non-space char.
   the return value is TRUE
   (pParseVars->status is unchanged)

If the end of input is encountered before a non-space char is found then
   the return value is FALSE
   pParseVars->status is set to STATUS_END_OF_STRING_ENCOUNTERED
   pParseVars->pInPJL_Local is set to point to the terminating 0.

If an FF is encountered before a non-space character is found then
   the return value is FALSE
   pParseVars->status is set to STATUS_REACHED_FF
   pParseVars->pInPJL_Local is set to point just past the FF.
*/
BOOL SkipOverSpaces(__in ParseVarsType *pParseVars)
{
LPSTR pIn = pParseVars->pInPJL_Local;
BYTE  in;

   while ( ((in=*pIn)==SPACE)&&(in!=0)&&(in!=FF) )
      {
      pIn++;
      }

   switch (in)
      {
      case FF:
         {
         pParseVars->status = STATUS_REACHED_FF;
         pParseVars->pInPJL_Local = pIn+1;
         return(FALSE);
         }
      case 0:
         {
         pParseVars->status = STATUS_END_OF_STRING;
         pParseVars->pInPJL_Local = pIn;
         return(FALSE);
         }
      default:
         {
         /* point to character after string in input */
         pParseVars->pInPJL_Local = pIn;
         return(TRUE);
         }
      }
}


void TokenFromParamValueFromNumberFF(
    __in    ParseVarsType*  pParseVars,
            ParamType       param
   )
{
   int value;

   StoreToken(pParseVars, param.token);
   if ( (value=GetPositiveInteger(pParseVars))==-1 )
      {
      /* Not a valid number - status set by GetPositiveInteger() */
      return;
      }
   if ( !StoreTokenValueAndAdvancePointer(pParseVars, value) )
      {
      return;
      }
   ExpectFinalCRLFFF(pParseVars);
   return;
}

void ActionNotFoundSkipPastFF(__in ParseVarsType *pParseVars)
{
   if ( SkipPastFF(pParseVars) )
      {
      pParseVars->status = STATUS_REACHED_END_OF_COMMAND_OK;
      }
   return;
}

/*
BOOL SkipPastFF(ParseVarsType *pParseVars)
This function skips over all characters until either a zero is found or
FF is found.

If the end of input is encountered before an FF char is found then
   the return value is FALSE
   pParseVars->status is set to STATUS_END_OF_STRING_ENCOUNTERED
   pParseVars->pInPJL_Local is set to point to the terminating 0.

If an FF is encountered
   the return value is TRUE
   pParseVars->status is set to STATUS_REACHED_FF
   pParseVars->pInPJL_Local is set to point just past the FF.
*/
BOOL SkipPastFF(__in ParseVarsType *pParseVars)
{
LPSTR pIn = pParseVars->pInPJL_Local;
BYTE  in;

   while ( ((in=*pIn)!=FF)&&(in!=0) )
      {
      pIn++;
      }

   if ( in==0 )
      {
      pParseVars->status = STATUS_END_OF_STRING;
      pParseVars->pInPJL_Local = pIn;
      return(FALSE);
      }
   pParseVars->pInPJL_Local = pIn+1;
   pParseVars->status = STATUS_REACHED_FF;
   return(TRUE);
}

void ExpectFinalCRLFFF(__in ParseVarsType *pParseVars)
{
   char c;

   if ( pParseVars->status==STATUS_CONTINUE )
      {
      c=*pParseVars->pInPJL_Local;
      if ( c==0 )
         {
         pParseVars->status = STATUS_END_OF_STRING;
         return;
         }

      if ( !AdvancePointerPastString(pParseVars, lpCRLF) )
         {
         if ( pParseVars->status==STATUS_REACHED_FF )
            {
            pParseVars->status = STATUS_SYNTAX_ERROR;
            }
         return;
         }
      ExpectFinalFF(pParseVars);
      }
   return;
}



void ExpectFinalFF(__in ParseVarsType *pParseVars)
{
   if ( pParseVars->status==STATUS_CONTINUE )
      {
      if ( *pParseVars->pInPJL_Local==FF )
         {
         pParseVars->status = STATUS_REACHED_END_OF_COMMAND_OK;
         pParseVars->pInPJL_Local++;
         }
      else
         {
         if ( *pParseVars->pInPJL_Local==0 )
            {
            pParseVars->status = STATUS_END_OF_STRING;
            }
         else
            {
            pParseVars->status = STATUS_SYNTAX_ERROR;
            }
         }
      }
   return;
}


/*
int GetPositiveInteger(ParseVarsType *pParseVars)
This function skips spaces and then interprets all the digits in input stream
as a positive integer.

If digits follow any spaces and they are not terminated by a zero then
   the return value is the positive integer.

If the first character following spaces in not a digit or the end of
string is encountered then
   -1 is returned as the value
   pParseVars->status is set to STATUS_SYNTAX_ERROR

Note: does not check for overflow
*/
int GetPositiveInteger(__in ParseVarsType *pParseVars)
{
   int   value;
   LPSTR pIn;
   BYTE  c;

   if ( !SkipOverSpaces(pParseVars) )
      {
      if ( pParseVars->status == STATUS_REACHED_FF )
         {
         pParseVars->status = STATUS_SYNTAX_ERROR;
         }
      return(-1);
      }

   pIn = pParseVars->pInPJL_Local;
   for ( value=0; ((c=*pIn++)>='0')&&(c<='9'); value=value*10+(c-'0') );
   if ( (c==0)||(pIn==pParseVars->pInPJL_Local+1) )
      {
      /* either end of string encountered or no digits found */
      if ( c==0 )
         {
         pParseVars->status = STATUS_END_OF_STRING;
         }
      else
         {
         pParseVars->status = STATUS_SYNTAX_ERROR;
         }
      pParseVars->pInPJL_Local = pIn-1;
      return(-1);
      }
   pParseVars->pInPJL_Local = pIn-1;
   return(value);
}



void SetNewList(__in ParseVarsType *pParseVars, ParamType param)
{
   pParseVars->arrayOfLists[0] = param.pList;
   pParseVars->arrayOfLists[1] = NULL;
   return;
}

void StoreToken(__in ParseVarsType *pParseVars, DWORD dwToken)
{
   pParseVars->dwNextToken = dwToken;
   return;
}

BOOL StoreTokenValueAndAdvancePointer(__in ParseVarsType *pParseVars, UINT_PTR dwValue)
{
   if ( pParseVars->nTokenLeft==0 )
      {
      pParseVars->status = STATUS_NOT_ENOUGH_ROOM_FOR_TOKENS;
      return(FALSE);
      }
   pParseVars->pToken_Local->token = pParseVars->dwNextToken;
   pParseVars->pToken_Local->value = dwValue;
   pParseVars->pToken_Local++;
   pParseVars->nTokenInBuffer_Local++;
   pParseVars->nTokenLeft--;
   return(TRUE);
}


void GetTotalAndLargestFF(__in ParseVarsType *pParseVars, ParamType param)
{
   int value;

   param; /* to eliminate not used warning */

   if ( !ExpectString(pParseVars, "TOTAL=") )
      {
      return;
      }
   StoreToken(pParseVars, TOKEN_INFO_MEMORY_TOTAL);
   if ( (value=GetPositiveInteger(pParseVars))==-1 )
      {
      /* Not a valid number - status set by GetPositiveInteger() */
      return;
      }
   if ( !StoreTokenValueAndAdvancePointer(pParseVars, value) )
      {
      return;
      }
   if ( !ExpectString(pParseVars, "\r\nLARGEST=") )
      {
      return;
      }
   StoreToken(pParseVars, TOKEN_INFO_MEMORY_LARGEST);
   if ( (value=GetPositiveInteger(pParseVars))==-1 )
      {
      /* Not a valid number - status set by GetPositiveInteger() */
      return;
      }
   if ( !StoreTokenValueAndAdvancePointer(pParseVars, value) )
      {
      return;
      }
   ExpectFinalCRLFFF(pParseVars);
   return;
}

void GetCodeAndOnlineFF(__in ParseVarsType *pParseVars, ParamType param)
{
   int value;

   param; /* to eliminate not used warning */

   if ( !ExpectString(pParseVars,"CODE=") )
      {
      return;
      }
   StoreToken(pParseVars, TOKEN_INFO_STATUS_CODE);
   if ( (value=GetPositiveInteger(pParseVars))==-1 )
      {
      /* Not a valid number - status set by GetPositiveInteger() */
      return;
      }
   if ( !StoreTokenValueAndAdvancePointer(pParseVars, value) )
      {
      return;
      }
   if ( !ExpectString(pParseVars, "\r\nDISPLAY=") )
      {
      return;
      }
   if ( !SkipPastNextCRLF(pParseVars) )
      {
      return;
      }
   if ( !ExpectString(pParseVars, "ONLINE=") )
      {
      return;
      }
   StoreToken(pParseVars, TOKEN_INFO_STATUS_ONLINE);
   pParseVars->pCurrentKeywords = FALSEandTRUEKeywords;
   if ( (value=LookForKeyword(pParseVars))==-1 )
      {
      /* Not TRUE or FALSE */
      pParseVars->status = STATUS_SYNTAX_ERROR;
      return;
      }
   if ( !StoreTokenValueAndAdvancePointer(pParseVars, value) )
      {
      return;
      }
   ExpectFinalCRLFFF(pParseVars);
   return;
}


/*
BOOL ExpectString(ParseVarsType *pParseVars, LPSTR pString)

This function looks for a match of the current stream
position with pString.

If a match is found:
   pParseVars->pInPJL_Local is set to point just past the string.
   the return value is TRUE
   (pParseVars->status is unchanged)

If the end of input is encountered before the string is found then
   pParseVars->pInPJL_Local is set to point to the terminating 0.
   the return value is FALSE
   pParseVars->status is set to STATUS_END_OF_STRING

If an FF is encountered before the string is found then
   pParseVars->pInPJL_Local is set to point just past the FF.
   the return value is FALSE
   pParseVars->status is set to STATUS_SYNTAX_ERROR
*/
BOOL ExpectString(__in ParseVarsType *pParseVars, __in LPSTR pString)
{
LPSTR pIn = pParseVars->pInPJL_Local;
LPSTR pS = pString;
BYTE  s, in = 0;

   while ( ((s=*pS) != 0) && ((in=*pIn)!=0) && (in!=FF) && (s==in) )
      {
      pS++;
      pIn++;
      }

   if ( s==0 )
      {
      /* The whole string matched  */
      /* point to character after string in input */
      pParseVars->pInPJL_Local = pIn;
      return(TRUE);
      }

   pParseVars->status = ( in!=0 )?
      STATUS_SYNTAX_ERROR:STATUS_END_OF_STRING;
      pParseVars->pInPJL_Local = pIn;
   return(FALSE);
}




/*
BOOL SkipPastNextCRLF(ParseVarsType *pParseVars)

This function positions the stream pointer past the next
CRLF.

If a CRLF is found:
   pParseVars->pInPJL_Local is set to point just past the CRLF.
   the return value is TRUE
   (pParseVars->status is unchanged)

If the end of input is encountered before the CRLF is found then
   pParseVars->pInPJL_Local is set to point to the terminating 0.
   the return value is FALSE
   pParseVars->status is set to STATUS_END_OF_STRING

If an FF is encountered before the CRLF is found then
   the return value is FALSE
   pParseVars->status is set to STATUS_SYNTAX_ERROR
*/
BOOL SkipPastNextCRLF(__in ParseVarsType *pParseVars)
{
   if ( !AdvancePointerPastString(pParseVars, "\r\n") )
      {
      if ( pParseVars->status == STATUS_REACHED_FF)
         {
         pParseVars->status = STATUS_SYNTAX_ERROR;
         }
      return(FALSE);
      }
   return(TRUE);
}


void GetTokenFromIndexSetNewList(__in ParseVarsType *pParseVars, ParamType param)
{
   StoreToken(pParseVars,
      pParseVars->pCurrentList->tokenBaseValue+pParseVars->dwFoundIndex);
   SetNewList(pParseVars, param);
   return;
}


void SetValueFromParamFF(__in ParseVarsType *pParseVars, ParamType param)
{
   if ( !StoreTokenValueAndAdvancePointer(pParseVars, param.value) )
      {
      return;
      }
   ExpectFinalCRLFFF(pParseVars);
   return;
}


void SetValueFromParam(__in ParseVarsType *pParseVars, ParamType param)
{
   if ( !StoreTokenValueAndAdvancePointer(pParseVars, param.value) )
      {
      return;
      }
   return;
}

void ActionNotFoundSkipCFLFandIndentedLines(__in ParseVarsType *pParseVars)
{
   DBG_MSG(DBG_TRACE, ("ActionNotFoundSkipCRLF In=%hs\n", pParseVars->pInPJL_Local));

   do
      {
      if ( !SkipPastNextCRLF(pParseVars) )
         {

         DBG_MSG(DBG_TRACE, ("ActionNotFoundSkipCRLF error skipping\n"));

         return;
         }
      } while (*pParseVars->pInPJL_Local==TAB);

   DBG_MSG(DBG_TRACE, ("ActionNotFoundSkipCRLF Out=%hs\n", pParseVars->pInPJL_Local));

   return;
}

void GetTokenFromIndexValueFromNumberEOLFromParam
   (__in ParseVarsType *pParseVars,ParamType param)
{
   int value;

   param; /* to eliminate not used warning */

   DBG_MSG(DBG_TRACE, ("GetTokenFromIndexValueFromNumberIn=%hs\n", pParseVars->pInPJL_Local));

   StoreToken(pParseVars,
      pParseVars->pCurrentList->tokenBaseValue+pParseVars->dwFoundIndex);
   if ( (value=GetPositiveInteger(pParseVars))==-1 )
      {
      /* Not a valid number - status set by GetPositiveInteger() */

      DBG_MSG(DBG_TRACE, ("error getting number\n"));

      return;
      }
   if ( !StoreTokenValueAndAdvancePointer(pParseVars, value) )
      {

      DBG_MSG(DBG_TRACE, ("error storing value\n"));

      return;
      }
   if ( !ExpectString(pParseVars, param.lpstr) )
      {
      return;
      }

   DBG_MSG(DBG_TRACE, ("GetTokenFromIndexValueFromNumberOut=%hs\n", pParseVars->pInPJL_Local));

   return;
}

void GetTokenFromIndexValueFromBooleanEOL
   (__in ParseVarsType *pParseVars,ParamType param)
{
   int value;

   param; /* to eliminate not used warning */

   DBG_MSG(DBG_TRACE, ("GetTokenFromIndexValueFromBooleanEOLin=%hs\n", pParseVars->pInPJL_Local));

   StoreToken(pParseVars,
      pParseVars->pCurrentList->tokenBaseValue+pParseVars->dwFoundIndex);
   pParseVars->pCurrentKeywords = FALSEandTRUEKeywords;

   if ( (value=LookForKeyword(pParseVars))==-1 )
      {
      /* Not TRUE or FALSE */
      pParseVars->status = STATUS_SYNTAX_ERROR;
      return;
      }
   if ( !StoreTokenValueAndAdvancePointer(pParseVars, value) )
      {
      return;
      }
   if ( !ExpectString(pParseVars, param.lpstr) )
      {
      return;
      }

   DBG_MSG(DBG_TRACE, ("GetTokenFromIndexValueFromBooleanEOLout=%hs\n", pParseVars->pInPJL_Local));

   return;
}

void GetTokenFromIndexValueFromStringEOL
   (__in ParseVarsType *pParseVars,ParamType param)
{
   param; /* to eliminate not used warning */

   DBG_MSG(DBG_TRACE, ("GetTokenFromIndexValueFromStringEOLin=%hs\n", pParseVars->pInPJL_Local));


   StoreToken(pParseVars,
      pParseVars->pCurrentList->tokenBaseValue+pParseVars->dwFoundIndex);

   if ( !StoreTokenValueAndAdvancePointer(pParseVars, (UINT_PTR)pParseVars->pInPJL_Local))
      {
      return;
      }
   SkipPastNextCRLF(pParseVars);

   DBG_MSG(DBG_TRACE, ("GetTokenFromIndexValueFromStringEOLout=%hs\n", pParseVars->pInPJL_Local));

   return;
}

